#include "wrapping_integers.hh"

// Dummy implementation of a 32-bit wrapping integer

// For Lab 2, please replace with a real implementation that passes the
// automated checks run by `make check_lab2`.

using namespace std;

//! Transform an "absolute" 64-bit sequence number (zero-indexed) into a WrappingInt32
//! \param n The input absolute 64-bit sequence number
//! \param isn The initial sequence number
WrappingInt32 wrap(uint64_t n, WrappingInt32 isn) {
    uint64_t offset = 1ul << 32;
    if (n >= offset)    n -= offset;
    n += isn.raw_value();
    return WrappingInt32{static_cast<uint32_t>(n & 0xffffffff)};
}

//! Transform a WrappingInt32 into an "absolute" 64-bit sequence number (zero-indexed)
//! \param n The relative sequence number
//! \param isn The initial sequence number
//! \param checkpoint A recent absolute 64-bit sequence number
//! \returns the 64-bit sequence number that wraps to `n` and is closest to `checkpoint`
//!
//! \note Each of the two streams of the TCP connection has its own ISN. One stream
//! runs from the local TCPSender to the remote TCPReceiver and has one ISN,
//! and the other stream runs from the remote TCPSender to the local TCPReceiver and
//! has a different ISN.
uint64_t unwrap(WrappingInt32 n, WrappingInt32 isn, uint64_t checkpoint) {
    uint64_t offset = 1ul << 32;
    uint64_t abs_seq = n.raw_value() + offset - isn.raw_value();
    if (abs_seq >= offset)      abs_seq -= offset;
    const uint64_t mask = 0xffffffff00000000;
    uint64_t base = checkpoint & mask;
    uint64_t res = base + abs_seq;
    if (res > checkpoint && res >= offset) {
        uint64_t tmp = res - offset;
        if (res - checkpoint > checkpoint - tmp)
            res = tmp;
    }
    if (res < checkpoint && res < mask) {
        uint64_t tmp = res + offset;
        if (checkpoint - res > tmp - checkpoint)
            res = tmp;
    }
    return res;
}
